#include "stdafx.h"
#ifdef _PARTICLES

#include "ParticleSys.h"
#include "RenderMiscs.h"
#include "ConfigManager.h"
#include "DecoLogger.h"
#include "VelocityFieldVisualizer.h"
//DWORD g_dwTlsParticleSys = TLS_OUT_OF_INDEXES;
#if defined _MPI_IMPLEMENT || defined _CONSOLE
#define PARTICLE_TRACE_STEPS 1
#else
#define PARTICLE_TRACE_STEPS 15
#endif

#define MAX_PARTICLE_LIFE 15

ParticleTrace::ParticleTrace() : mPosTrace(NULL), mVelTrace(NULL)
{

}
ParticleTrace::~ParticleTrace()
{

}
 
ParticleTrace::ParticleTrace(const ParticleTrace& rhs) : mNumTraceSteps(rhs.mNumTraceSteps), mPosTrace(rhs.mPosTrace),	mVelTrace(rhs.mVelTrace)
{
	
}

const ParticleTrace& ParticleTrace::operator=(const ParticleTrace& rhs)
{
	if (&rhs == this)
		return *this;
	mNumTraceSteps = rhs.mNumTraceSteps;
	mPosTrace = rhs.mPosTrace;
	mVelTrace = rhs.mVelTrace;
	return *this;
}



void ParticleTrace::SetTraceSteps(int steps)
{
	mNumTraceSteps = steps;
	mPosTrace.SetLength(steps);
	mVelTrace.SetLength(steps);

}

void ParticleTrace::GetWholeTrace(std::vector<vector3>& trace) const
{
	mPosTrace.GetAllElements(trace);
}

const vector3& ParticleTrace::GetPosAtIthStep(int ithStep) const
{
	return mPosTrace.GetIthElement(mNumTraceSteps + ithStep);
}
const vector3& ParticleTrace::GetVelAtIthStep(int ithStep) const
{
	return mVelTrace.GetIthElement(mNumTraceSteps + ithStep);
}
void ParticleTrace::Update(const vector3& pos, const vector3& vel)
{
	mPosTrace.PushBack(pos);
	mVelTrace.PushBack(vel);
}


Particle::Particle() : m_pos(vector3(0.f, 0.f, 0.f)), m_prePos(vector3(0.f, 0.f, 0.f)), m_velocity(vector3(0.f, 0.f, 0.f)), m_life(MAX_PARTICLE_LIFE), m_vorticity(0)
{
	m_trace.SetTraceSteps(PARTICLE_TRACE_STEPS);
}

Particle::Particle(const vector3& pos) : m_pos(pos), m_prePos(pos), m_velocity(vector3(0.f, 0.f, 0.f)), m_life(MAX_PARTICLE_LIFE), m_vorticity(0)
{
	m_trace.SetTraceSteps(PARTICLE_TRACE_STEPS);
}


void ParticleSys::SetScene(DecoScene* scene)
{
	m_scene = scene;
}


void ParticleSys::SetGridField( GridField* gridField )
{
	m_grid = gridField;
}

void ParticleSys::SetSolver(NSSolver* solver)
{
	m_solver = solver;
}

void ParticleSys::Evolve ()
{
	DOUBLE TIME_STEP = m_scene->GetTimeStep();
	int count = 0, adjusted = 0, cancelled = 0, maxCount = 0, bornDead = 0, notMoved = 0;
	double maxX = 0;
	INT bTetSprayer = 0, iterateTimes = 3;
	DecoConfig::GetSingleton()->GetInt( "Solver", "bTetSprayer", bTetSprayer );
	vector3 sprayerVel;
	vector3 sprayerCenter = vector3(0,0,0), sprayerExtent = vector3(0,0,0),
		freerCenter = vector3(0,0,0), freerExtent = vector3(0,0,0);
	if ( bTetSprayer ){
		DecoConfig::GetSingleton()->GetVector3( "Sprayer", "velocity", sprayerVel );
		DecoConfig::GetSingleton()->GetInt( "Particle", "iterateTimes", iterateTimes );
		DecoSceneObject *sprayerObj = m_scene->GetSceneObj( "sprayer" );
		DecoSceneObject *freerObj = m_scene->GetSceneObj( "freer" );
		if ( sprayerObj ) {
		    sprayerCenter = sprayerObj->GetRenderData()->GetBoundingBox().GetCenter();
		    sprayerExtent = sprayerObj->GetRenderData()->GetBoundingBox().GetExtent();
		}
		if ( freerObj ){
			//*(DecoLogger::GetSingleton())<<"There is a freeer\n";
			freerCenter = freerObj->GetRenderData()->GetBoundingBox().GetCenter();
			freerExtent = freerObj->GetRenderData()->GetBoundingBox().GetExtent();
		}
	}

	for (list<Particle>::iterator it = particles.begin(); it != particles.end();)
	{
		if ((*it).m_life != MAX_PARTICLE_LIFE)
		{
			if ((*it).m_life <= 0)
			{
				list<Particle>::iterator tmpIt = it;
				++it;
				particles.erase(tmpIt);
			}
			else
			{				
				(*it).m_life--;

				vector3 pos = (*it).m_pos;
				vector3 vel = (*it).m_velocity;
				vector3 newPos(pos + static_cast<FLOAT>(TIME_STEP) * vel);

				(*it).m_prePos = pos;
				(*it).m_pos = newPos;
//				(*it).m_vorticity = m_grid->SampleScalerValue(newPos, m_grid->mVorticity);
				(*it).m_trace.Update(newPos, vel);
				++it;
			}
		}
		else
		{
			vector3 pos = (*it).m_pos;
			vector3 vel = m_solver->GetVelocity( pos, TRUE );
			vector3 midPoint(pos + static_cast<FLOAT>(TIME_STEP) * vel / 2.0);
			vector3 midVel = m_solver->GetVelocity( midPoint, TRUE );
			vector3 newPos(pos + static_cast<FLOAT>(TIME_STEP) * midVel);
			const vector3& Min = m_grid->scnBox.GetMin();
			const vector3& Max = m_grid->scnBox.GetMax();
			if ( newPos.x < Min.x ){
				newPos.x = Min.x;
			}
			else if ( newPos.x > Max.x ){
				newPos.x = Max.x;
			}
			if ( newPos.y < Min.y ){
				newPos.y = Min.y;
			}
			else if ( newPos.y > Max.y ){
				newPos.y = Max.y;
			}
			if ( newPos.z < Min.z ){
				newPos.z = Min.z;
			}
			else if ( newPos.z > Max.z ){
				newPos.z = Max.z;
			}

			(*it).m_prePos = pos;
			(*it).m_pos = newPos;
			(*it).m_velocity = midVel;
			(*it).m_vorticity = m_grid->SampleScalerValue(newPos, m_grid->mVorticity);
			(*it).m_trace.Update(newPos, midVel);
			++it;
		}
	}
}

void ParticleSys::DumpParticleTraces(const string& filePath)
{
	VelocityFieldVisualizer visualizer(m_grid, this);
	visualizer.SaveToFile(filePath + ".trace");
}

void ParticleSys::Render (DecoRenderInterface* RI, DecoLight** Lights, UINT numEffectiveLights, RenderProfileT profile)
{
#if 1
	VelocityFieldVisualizer visualizer(m_grid, this);
	visualizer.Render(RI, Lights, numEffectiveLights);

#else
	if (particles.size())
	{	
		vector<vector3> particlePos;
		vector<DecoColor> particleCol;
		UINT i = 0;
		int count = 0;
		//double maxVort = m_grid->GetMaxVorticiyMagnitute();
		//double maxPressure = m_grid->GetMaxPressureMagnitute();
		//double minPressure = m_grid->GetMinPressureMagnitute();
		//double pressureDist = maxPressure - minPressure;
		DecoColor col(255, 255, 255);

		for (list<Particle>::const_iterator it = particles.begin(); it != particles.end();++it)
		{
			count++;

			//if (count % 20)
			//	continue;
			if (profile == RP_Vorticity)
			{
				double vorticityValue = m_grid->SampleScalerValue((*it).m_pos, m_grid->mVorticity);
				double vortMag = 50 * vorticityValue;
				if (vortMag > 255)
					vortMag = 255;
				if (vortMag < -255)
					vortMag = -255;
				//int threshold = 50;
				//if (abs(vortMag) < threshold)
				//	continue;
				double ratio = (vortMag + 255) / (255 * 2);
				col = DecoColor(static_cast<BYTE>(ratio * 255), 0, static_cast<BYTE>((1 - ratio) * 255));

				//double ratio = abs(vortMag) / 255.0;
				//if (vortMag > 0)
				//	col = DecoColor(static_cast<BYTE>(ratio * 255), 0, 0);
				//else
				//	col = DecoColor(0, 0, static_cast<BYTE>(ratio * 255));
			}
			else if (profile == RP_Pressure)
			{
				double pressure = m_grid->SamplePressure((*it).m_pos);
				double pressureMag = pressure / 200/** 5*/;
				if (pressureMag > 255)
					pressureMag = 255;
				if (pressureMag < -255)
					pressureMag = -255;
				int threshold = 0;
				if (abs(pressureMag) < threshold)
					continue;
				double ratio = (pressureMag + 255) / (255 * 2);
				col = DecoColor(static_cast<BYTE>(ratio * 255), 0, static_cast<BYTE>((1 - ratio) * 255));
			}
			particleCol.push_back(col);
			particlePos.push_back((*it).m_pos);


		}
		if (!particlePos.empty())
			DecoRenderMisc::GetSingleton()->DrawPointList(NULL, &particlePos[0], &particleCol[0], static_cast<UINT>(particlePos.size()), 1);
	}
#endif
}

void ParticleSys::fillParticlesInCube(const vector3& basePos, DOUBLE stepSize, DOUBLE numParPerCell)
{
	if (numParPerCell < 1.0)
	{
		double randomNum = RandDouble(0.0, 1.0);
		if (randomNum < numParPerCell)
		{
			vector3 particle;
			particle.x = basePos.x + (rand() % static_cast<INT>(stepSize * 10000)) / 10000.f;
			particle.y = basePos.y + (rand() % static_cast<INT>(stepSize * 10000)) / 10000.f;
			particle.z = basePos.z + (rand() % static_cast<INT>(stepSize * 10000)) / 10000.f;

			Particle p(particle);
			particles.push_back(p);

		}
	}
	else
	{
		for (INT ithPar = 0; ithPar < static_cast<int>(numParPerCell); ithPar++)
		{
			vector3 particle;
			particle.x = basePos.x + (rand() % static_cast<INT>(stepSize * 10000)) / 10000.f;
			particle.y = basePos.y + (rand() % static_cast<INT>(stepSize * 10000)) / 10000.f;
			particle.z = basePos.z + (rand() % static_cast<INT>(stepSize * 10000)) / 10000.f;
			//particle.x = basePos.x + (i + 1) * m_grid->stepSize / (PARTICLE_PER_COORD + 1);
			//particle.y = basePos.y + (j + 1) * m_grid->stepSize / (PARTICLE_PER_COORD + 1);
			//particle.z = basePos.z + (k + 1) * m_grid->stepSize / (PARTICLE_PER_COORD + 1);

			Particle p(particle);
			particles.push_back(p);

		}

	}
}

void ParticleSys::AddParticle(const Particle& p)
{
	particles.push_back(p);

}

void ParticleSys::FillParticles(BYTE*** solidElements, INT numSlices, INT height, INT width)
{ 
	//particles.clear();
	INT numParPerUnit = 0;
	DecoConfig::GetSingleton()->GetInt("Particle", "numPerUnit", numParPerUnit);

	GridField* grid = m_grid;



	if (numSlices >= grid->numXElement)
	{		
		INT xRatio = numSlices / grid->numXElement;
		INT yRatio = height / grid->numYElement;
		INT zRatio = width / grid->numZElement;
		if (xRatio != yRatio || yRatio != zRatio)
		{
			(*DecoLogger::GetSingleton()) << "Warning: FillParticles failed because xRatio, yRatio and zRatio differ.\n";
		}
		DOUBLE stepSize = m_grid->scnBox.GetExtent().x * 2 / numSlices;
		INT numParPerCell = static_cast<INT>(stepSize * stepSize * stepSize * numParPerUnit);
		for (list<Particle>::iterator it = particles.begin(); it != particles.end(); )
		{
			vector3 pos = (*it).m_pos;
			pos -= m_grid->scnBox[0];
			INT i = static_cast<INT>(pos.x / stepSize);
			INT j = static_cast<INT>(pos.y / stepSize);
			INT k = static_cast<INT>(pos.z / stepSize);
			if (solidElements[i][j][k] > 0)
			{
				list<Particle>::iterator tmpIt = it;
				++it;
				particles.erase(tmpIt);
			}
			else
				++it;
		}
		for (INT i = 0; i < numSlices; i++)
		{
			for (INT j = 0; j < height; j++)
			{
				for (INT k = 0; k < width; k++)
				{
					if (solidElements[i][j][k] > 0)
					{

						INT xIndex, yIndex, zIndex;
						xIndex = i / xRatio;
						yIndex = j / yRatio;
						zIndex = k / zRatio;
						if (grid->type[xIndex][yIndex][zIndex] == GT_Solid)
							continue;
						vector3 basePos(m_grid->scnBox[0].x + i * stepSize, m_grid->scnBox[0].y + j * stepSize, m_grid->scnBox[0].z + k * stepSize);
						fillParticlesInCube(basePos, stepSize, numParPerCell);
					}	
				}
			}
		}
	}
	else
	{
		INT xRatio = grid->numXElement / numSlices;
		INT yRatio = grid->numYElement / height;
		INT zRatio = grid->numZElement / width;
		if (xRatio != yRatio || yRatio != zRatio)
		{
			(*DecoLogger::GetSingleton()) << "Warning: FillParticles failed because xRatio, yRatio and zRatio differ.\n";
		}
		DOUBLE stepSize = grid->stepSize;
		INT numParPerCell = static_cast<INT>(stepSize * stepSize * stepSize * numParPerUnit);
		for (list<Particle>::iterator it = particles.begin(); it != particles.end(); )
		{
			vector3 pos = (*it).m_pos;
			pos -= m_grid->scnBox[0];
			INT i = static_cast<INT>(pos.x / stepSize) / xRatio;
			INT j = static_cast<INT>(pos.y / stepSize) / yRatio;
			INT k = static_cast<INT>(pos.z / stepSize) / zRatio;
			if (solidElements[i][j][k] > 0)
			{
				if (solidElements[i][j][k] > 0)
				{
					list<Particle>::iterator tmpIt = it;
					++it;
					particles.erase(tmpIt);
				}
			}				
			else
				++it;
		}
		for (INT i = 0; i < grid->numXElement; i++)
		{
			for (INT j = 0; j < grid->numYElement; j++)
			{
				for (INT k = 0; k < grid->numZElement; k++)
				{
					INT ithElement = i / xRatio;
					INT jthElement = j / yRatio;
					INT kthElement = k / zRatio;
					if (solidElements[ithElement][jthElement][kthElement] > 0)
					{

						if (grid->type[i][j][k] == GT_Solid) 
							continue;
						vector3 basePos(m_grid->scnBox[0].x + i * stepSize, m_grid->scnBox[0].y + j * stepSize, m_grid->scnBox[0].z + k * stepSize);
						fillParticlesInCube(basePos, stepSize, numParPerCell);
					}	
				}
			}
		}

	}
}

void ParticleSys::FillParticles()
{
	particles.clear();

	int numX = m_grid->numXElement;
	int numY = m_grid->numYElement;
	int numZ = m_grid->numZElement;
	double stepSize = m_grid->stepSize;

	INT numParPerUnit = 0;
	DecoConfig::GetSingleton()->GetInt("Particle", "numPerUnit", numParPerUnit);
	double numParPerCell = numParPerUnit * pow(stepSize, 3);
	int midCell = numZ / 2;
	for (int i = 0; i < numX; ++i)
	{
		for (int j = 0; j < numY; ++j)
		{
			for (int k = 0; k < numZ; ++k)
			{
				//if (abs(k - midCell) > 2)
				//	continue;
				if (m_grid->isFluidCell(i, j, k))
				{
					vector3 basePos(m_grid->scnBox[0].x + i * stepSize, m_grid->scnBox[0].y + j * stepSize, m_grid->scnBox[0].z + k * stepSize);
					fillParticlesInCube(basePos, stepSize, numParPerCell);
				}
			}
		}
	}
}

void ParticleSys::FillParticlesInBox(const Box& box)
{
	int numX = m_grid->numXElement;
	int numY = m_grid->numYElement;
	int numZ = m_grid->numZElement;
	double stepSize = m_grid->stepSize;

	INT numParPerUnit = 0;
	DecoConfig::GetSingleton()->GetInt("Particle", "numPerUnit", numParPerUnit);
	double numParPerCell = numParPerUnit * pow(stepSize, 3);
	int midCell = numZ / 2;
	for (int i = 0; i < numX; ++i)
	{
		for (int j = 0; j < numY; ++j)
		{
			for (int k = 0; k < numZ; ++k)
			{

				if (m_grid->isFluidCell(i, j, k))
				{
					vector3 basePos(m_grid->scnBox[0].x + i * stepSize, m_grid->scnBox[0].y + j * stepSize, m_grid->scnBox[0].z + k * stepSize);
					if (checkWithInBox(basePos + vector3(stepSize / 2.0, stepSize / 2.0, stepSize / 2.0), box.GetMin(), box.GetMax()))
						fillParticlesInCube(basePos, stepSize, numParPerCell);
				}
			}
		}
	}

}

void ParticleSys::EraseParticlesOutsideBox(const Box& box)
{
	for (std::list<Particle>::iterator it = particles.begin(); it != particles.end(); ++it)
	{
		Particle& p = (*it);
		if (!checkWithInBox(p.m_pos, box.GetMin(), box.GetMax()))
		{
			p.m_life--;
		}
		//{
		//	std::list<Particle>::iterator prevIt = it;
		//	++it;
		//	particles.erase(prevIt);
		//}
		//else
		//	++it;
	}
}



const list<Particle>&  ParticleSys::GetAllParticles()
{
	return particles;
}
 

void ParticleSys::DumpRIB(const char* filename)
{
	ofstream out(filename, ios::out);
	out<<"Points ";

	out<<"\"P\" [";
	for (list<Particle>::const_iterator it = particles.begin(); it != particles.end(); ++it)
		out<<(*it).m_pos.x<<" "<<(*it).m_pos.y<<" "<<(*it).m_pos.z<<" ";
	out<<"] ";
}

void ParticleSys::Serialize(DecoArchive& Ar) const
{
	string filePath = Ar.GetFilePath();
	char lowerpath[256];
	size_t i;
	for(i = 0; i < filePath.length(); ++i)
	{
		lowerpath[i] = tolower(filePath[i]);
	}
	lowerpath[i] = '\0';
	char* c = NULL;
	if((c = strstr(lowerpath, ".dat")) > 0)
	{
		c[0] = '.';
		c[1] = 'p';
		c[2] = 'a';
		c[3] = 'r';
	}
	DecoArchive newAr(lowerpath, AT_Write);
	INT particleNum = static_cast<INT>(particles.size());
	newAr << particleNum;
	for (list<Particle>::const_iterator it = particles.begin(); it != particles.end(); ++it)
	{
		newAr << (*it);
	}

}
void ParticleSys::Deserialize(DecoArchive& Ar)
{
	string filePath = Ar.GetFilePath();
	char lowerpath[256];
	size_t i;
	for(i = 0; i < filePath.length(); ++i)
	{
		lowerpath[i] = tolower(filePath[i]);
	}
	lowerpath[i] = '\0';
	char* c = NULL;
	if((c = strstr(lowerpath, ".dat")) > 0)
	{
		c[0] = '.';
		c[1] = 'p';
		c[2] = 'a';
		c[3] = 'r';
	}
	DecoArchive newAr(lowerpath, AT_Read);
	INT particleNum = 0;
	newAr >> particleNum;
	for (INT i = 0; i < particleNum; i++)
	{
		Particle p;
		newAr >> p;
		particles.push_back(p);
	}
}

void ParticleSys::ReadFromFile()
{
	string particlesFile;
	DecoConfig::GetSingleton()->GetString( "ParticlesFromFile", "particlesFilePath", particlesFile );
	if ( particlesFile.empty() ){
		return;
	}
	else {
		char lowerpath[256];
		size_t i;
		for(i = 0; i < particlesFile.length(); ++i)
		{
			lowerpath[i] = tolower(particlesFile[i]);
		}
		lowerpath[i] = '\0';
		DecoArchive newAr(lowerpath, AT_Read);
		INT particleNum = 0;
		newAr >> particleNum;
		for (INT i = 0; i < particleNum; i++)
		{
			Particle p;
			newAr >> p;
			particles.push_back(p);
		}
	}
}

//ParticleSys* ParticleSys::GetSingleton()
//{
//	if (g_dwTlsParticleSys == TLS_OUT_OF_INDEXES)
//	{
//		g_dwTlsParticleSys = TlsAlloc();
//		TlsSetValue(g_dwTlsParticleSys, NULL);
//	}
//
//	ParticleSys* p = (ParticleSys*)TlsGetValue(g_dwTlsParticleSys);
//	if (p)
//		return p;
//	else
//	{
//		p = new ParticleSys();
//		TlsSetValue(g_dwTlsParticleSys, p);
//		return p;
//	}
//}
//
//void ParticleSys::DestroySingleton()
//{
//	if (g_dwTlsParticleSys == TLS_OUT_OF_INDEXES)
//		return;
//	else
//	{
//		ParticleSys *pDM = (ParticleSys*)TlsGetValue(g_dwTlsParticleSys);
//		if (pDM != NULL)
//		{
//			delete pDM;
//			TlsSetValue(g_dwTlsParticleSys, NULL);
//		}
//	}
//}

#endif
